/*
iovpn
*/
package main

import (
	"flag"
	"fmt"
	"log"
	"net"
	"os"
	"os/exec"
	"strings"
	"sync"

	"github.com/songgao/water"
)

type config struct {
	devtype    string
	devnetwork string
	kind       string
	localaddr  string
	peeraddrs  string
}

var (
	conmap = &sync.Map{}
	Conf   = &config{}
)

func init() {
	flag.StringVar(&Conf.devtype, "dev", "tun", "type of tuntap device[tap|tun] ")
	flag.StringVar(&Conf.devnetwork, "devip", "10.0.0.1/24", "ip of tun/tap")
	flag.StringVar(&Conf.kind, "kind", "server", "is server or client")
	flag.StringVar(&Conf.localaddr, "local", "0.0.0.0:8001", "local  address")
	flag.StringVar(&Conf.peeraddrs, "peer", "127.0.0.1:8001", "peer address")
	flag.Parse()
}

func main() {
	ifio := createtuntap(Conf.devtype, Conf.devnetwork)
	server(Conf.localaddr, strings.Split(Conf.peeraddrs, ","), ifio)
}

func createtuntap(devtype string, devnetwork string) water.Interface {
	// 选择tuntap类型，及设置配置
	config := water.Config{}
	switch devtype {
	case "tap":
		config.DeviceType = water.TAP
	default:
		config.DeviceType = water.TUN
	}
	// 根据配置生成接口
	ifce, err := water.New(config)
	if err != nil {
		log.Fatal(err)
	}
	err = setupNetDevice("addr", "add", devnetwork, "dev", ifce.Name())
	if err != nil {
		fmt.Println("网卡[", ifce.Name(), "]设置网络地址失败：", err)
	} else {
		fmt.Println("网卡[", ifce.Name(), "]设置地址：", devnetwork)
	}
	err = setupNetDevice("link", "set", ifce.Name(), "mtu", "1500", "up")
	if err != nil {
		fmt.Println("网卡[", ifce.Name(), "]启动失败：", err)
	} else {
		fmt.Println("网卡[", ifce.Name(), "]启动成功")
	}
	return *ifce
}

func setupNetDevice(cmd ...string) error {
	cmdexec := exec.Command("ip", cmd...)
	cmdexec.Stderr = os.Stderr
	cmdexec.Stdout = os.Stdout
	err := cmdexec.Run()
	if err != nil {
		return err
	}
	return nil
}

/*udp*/
func server(laddr string, peers []string, ifio water.Interface) {
	defer ifio.Close()

	// 解析本地地址
	locaddr, err := net.ResolveUDPAddr("udp", laddr)
	if err != nil {
		log.Fatalln(err)
		return
	}
	listener, err := net.ListenUDP("udp", locaddr)
	if err != nil {
		log.Fatalln(err)
		return
	}

	// 解析节点地址
	if len(peers) > 0 {
		for _, peer := range peers {
			paddr, err := net.ResolveUDPAddr("udp", peer)
			if err != nil {
				log.Fatalln(err)
				return
			}
			conmap.Store(paddr.String(), paddr)
			fmt.Println("[init添加peer]：==> ", paddr.String())
		}
	}

	// udp --> net device
	go func() {
		buf := make([]byte, 1500)
		for {
			n, clientaddr, err := listener.ReadFromUDP(buf)
			if err != nil {
				log.Fatalln(err)
				continue
			}
			_, ok := conmap.LoadOrStore(clientaddr.String(), clientaddr)
			if !ok {
				fmt.Println("[  添加peer  ]：==> ", clientaddr.String())
			}
			ifio.Write(buf[:n])
		}
	}()

	// net device --> udp
	func() {
		buf := make([]byte, 1500)
		for {
			n, err := ifio.Read(buf)
			if err != nil {
				log.Fatalln(err)
				continue
			}
			conmap.Range(func(key, value interface{}) bool {
				_, err := listener.WriteToUDP(buf[:n], value.(*net.UDPAddr))
				if err != nil {
					conmap.Delete(key)
					fmt.Println("[  删除peer  ]：==> ", key.(string))
					return true
				}
				return true
			})
		}
	}()
}
